home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- send.c
-
- This module handles sending postings and mail messages.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <string.h>
-
- #include "glob.h"
- #include "dialog.h"
- #include "news.h"
- #include "send.h"
- #include "smtp.h"
- #include "header.h"
- #include "newswatcher.h"
- #include "status.h"
- #include "message.h"
- #include "charset.h"
- #include "cancel.h"
- #include "memutil.h"
- #include "strutil.h"
- #include "ic.h"
-
-
-
- #define kPostAlert 135 /* Post confirm dialog */
- #define kPostCancelAlert 147 /* Post cancel dialog */
-
-
-
- /*----------------------------------------------------------------------------
- Wrap
-
- Wraps paragraphs to lines of at most 74 characters long.
-
- Entry: text = handle to text to be wrapped.
- start = offset in text of beginning of text to be wrapped.
- end = offset in text of end of text to be wrapped.
-
- Only lines which which exceed 80 characters in length are wrapped.
- ----------------------------------------------------------------------------*/
-
- void Wrap (Handle text, long start, long end)
- {
- char *p, *pEnd, *q, *lastSpace;
-
- p = *text + start;
- pEnd = *text + end;
- while (p < pEnd) {
- q = p;
- while (q < pEnd && *q != CR) q++;
- if (q - p > 80) {
- q = p;
- lastSpace = 0;
- while (true) {
- while (q < pEnd && *q != ' ' && *q != CR) q++;
- if (q < pEnd && *q == ' ') {
- while (q < pEnd && *q == ' ') q++;
- q--;
- }
- if (q - p >= 75 && lastSpace != 0) {
- *lastSpace = CR;
- p = lastSpace + 1;
- lastSpace = 0;
- }
- if (q >= pEnd || *q == CR) break;
- lastSpace = q;
- q++;
- }
- }
- p = q+1;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- UnWrap
-
- Unwraps paragraphs.
-
- Entry: text = handle to text to be unwrapped.
- start = offset in text of beginning of text to be unwrapped.
- end = offset in text of end of text to be unwrapped.
- ----------------------------------------------------------------------------*/
-
- void UnWrap (Handle text, long start, long end)
- {
- char *p, *pEnd;
-
- p = *text + start;
- pEnd = *text + end;
- while (p < pEnd) {
- while (p < pEnd && *p != CR) p++;
- p++;
- if (p < pEnd) {
- if (*p == ' ' || *p == '\t') continue;
- if (*p == CR) {
- while (p < pEnd && *p == CR) p++;
- continue;
- }
- *(p-1) = ' ';
- }
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetRecipients
-
- Gets recipient addresses for an email message.
-
- Entry: text = message to be mailed, including headers.
- hdr = C-format message header to process (To, Cc, or Bcc).
-
- Exit: function result = error code.
- *recipients = handle to comma-separated list of
- recipient email addresses, or nil if header not found.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetRecipients (Handle text, char *hdr, Handle *recipients)
- {
- Handle recip;
- OSErr err = noErr;
- char *p, *pEnd, *q, *r, *begin, *end;
- long len;
- short parenLevel;
- Boolean insideQuotedString = false;
-
- err = FindHeaderHandle(text, hdr, &recip);
- if (err != noErr) return err;
- if (recip == nil) {
- *recipients = nil;
- return noErr;
- }
-
- /* Strip comments in parentheses and blanks. */
-
- p = q = *recip;
- len = MyGetHandleSize(recip);
- pEnd = p + len;
- while (p < pEnd) {
- if (*p == '(') {
- parenLevel = 1;
- p++;
- while (p < pEnd && parenLevel > 0) {
- if (*p == '(') {
- parenLevel++;
- } else if (*p == ')') {
- parenLevel--;
- }
- p++;
- }
- } else if (*p == ' ') {
- p++;
- } else {
- *q++ = *p++;
- }
- }
- len = q - *recip;
-
- /* Extract recipients */
-
- p = q = *recip;
- pEnd = p + len;
- while (p < pEnd) {
- r = p;
- while (r < pEnd) {
- if (*r == '"') {
- insideQuotedString = true;
- r++;
- while (r < pEnd && *r != '"') r++;
- if (r >= pEnd) break;
- insideQuotedString = false;
- r++;
- } else if (*r == '<' || *r == ',') {
- break;
- } else {
- r++;
- }
- }
- if (insideQuotedString) break;
- if (r >= pEnd || *r == ',') {
- begin = p;
- end = r-1;
- } else {
- r++;
- begin = r;
- while (r < pEnd && *r != '>') r++;
- end = r-1;
- }
- p = r;
- while (p < pEnd && *p != ',') p++;
- if (*p == ',') p++;
- len = end - begin + 1;
- if (q != *recip) *q++ = ',';
- BlockMoveData(begin, q, len);
- q += len;
- }
- len = q - *recip;
- MySetHandleSize(recip, len);
- *recipients = recip;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SendMailMessage
-
- Send a mail message.
-
- Entry: text = message to be mailed, including headers.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SendMailMessage (Handle text)
- {
- SmtpStreamRef s;
- OSErr err = noErr;
- Handle to = nil;
- Handle cc = nil;
- Handle bcc = nil;
- long toLen, ccLen, bccLen, len;
- char *q;
- NetServerErrInfo serverErrInfo;
-
- MyICReadSharedPrefs(kICEmail);
- MyICReadSharedPrefs(kICSMTPHost);
-
- err = GetRecipients(text, "To", &to);
- if (err != noErr) goto exit1;
- err = GetRecipients(text, "Cc", &cc);
- if (err != noErr) goto exit1;
- err = GetRecipients(text, "Bcc", &bcc);
- if (err != noErr) goto exit1;
-
- toLen = to == nil ? 0 : MyGetHandleSize(to);
- ccLen = cc == nil ? 0 : MyGetHandleSize(cc);
- bccLen = bcc == nil ? 0 : MyGetHandleSize(bcc);
-
- len = toLen;
- if (cc != nil) {
- if (len > 0) len++;
- len += ccLen;
- }
- if (bcc != nil) {
- if (len > 0) len++;
- len += bccLen;
- }
- len++;
-
- if (to == nil) {
- err = MyNewHandle(len, &to);
- } else {
- err = MySetHandleSize(to, len);
- }
- if (err != noErr) goto exit1;
-
- q = *to + toLen;
- if (cc != nil) {
- if (q > *to) *q++ = ',';
- BlockMoveData(*cc, q, ccLen);
- q += ccLen;
- }
- if (bcc != nil) {
- if (q > *to) *q++ = ',';
- BlockMoveData(*bcc, q, bccLen);
- q += bccLen;
- }
- *q = 0;
-
- p2cstr(gPrefs.mailServerName);
- err = SmtpOpen((char*)gPrefs.mailServerName, &s);
- c2pstr((char*)gPrefs.mailServerName);
- if (err != noErr) goto exit2;
-
- MyHLock(to);
- err = SmtpSendMessage(s, gPrefs.emailAddress, *to, text);
- MyHUnlock(to);
- if (err != noErr) goto exit2;
-
- err = SmtpClose(s);
- if (err != noErr) goto exit2;
-
- MyDisposeHandle(to);
- MyDisposeHandle(cc);
- MyDisposeHandle(bcc);
-
- return noErr;
-
- exit1:
-
- MyDisposeHandle(to);
- MyDisposeHandle(cc);
- MyDisposeHandle(bcc);
- return err;
-
- exit2:
-
- MyDisposeHandle(to);
- MyDisposeHandle(cc);
- MyDisposeHandle(bcc);
- if (err == smtpServerErr) {
- SmtpGetServerErrInfo(s, &serverErrInfo);
- SmtpClose(s);
- ServerErrorMessage(kStrMail, serverErrInfo.command, serverErrInfo.response);
- return userCanceledErr;
- } else {
- p2cstr(gPrefs.mailServerName);
- SaveNetErrorInfo(kStrMail, (char*)gPrefs.mailServerName);
- c2pstr((char*)gPrefs.mailServerName);
- return err;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- AreYouSure
-
- Present "Are you sure you want to post" alert.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr AreYouSure (void)
- {
- DialogPtr dlg = nil;
- short item;
- OSErr err = noErr;
-
- if (!gPrefs.areYouSureAlert) return noErr;
- err = MyGetNewDialog(kPostAlert, ok, cancel, &dlg);
- if (err != noErr) return err;
- SysBeep(0);
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- err = DoClose(dlg);
- if (err != noErr) return err;
- return item == ok ? noErr : userCanceledErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- BuildMessageForPrinting
-
- Build a message for printing.
-
- Entry: wind = pointer to message window.
-
- Exit: function result = error code.
- *messageText = handle to message text to be printed.
- ----------------------------------------------------------------------------*/
-
- OSErr BuildMessageForPrinting (WindowPtr wind, Handle *messageText)
- {
- TWindow **info;
- TEHandle theTE;
- Handle text = nil;
- Handle msg = nil;
- Handle newsgroups, to, subject, cc, bcc, replyto;
- Handle followupto, keywords, distribution;
- Handle extraMail, signature, references;
- OSErr err = noErr, err1 = noErr;
- long sigLen, textLen, newTextLen;
- char *q;
- Boolean newsIcon, mailIcon, selfIcon;
-
- info = (TWindow**)GetWRefCon(wind);
- theTE = (**info).theTE;
- text = (**theTE).hText;
- newsgroups = (**(**info).newsgroupsField).hText;
- to = (**(**info).toField).hText;
- subject = (**(**info).subjectField).hText;
- cc = (**(**info).ccField).hText;
- bcc = (**(**info).bccField).hText;
- replyto = (**(**info).replytoField).hText;
- followupto = (**(**info).followuptoField).hText;
- keywords = (**(**info).keywordsField).hText;
- distribution = (**(**info).distributionField).hText;
- extraMail = (**(**info).extraMailField).hText;
- signature = (**(**info).signatureField).hText;
- references = (**info).references;
- newsIcon = (**info).newsIcon;
- mailIcon = (**info).mailIcon;
- selfIcon = (**info).selfIcon;
-
- err = MyHandToHand(&text);
- if (err != noErr) return err;
-
- textLen = MyGetHandleSize(text);
- q = *text + textLen - 1;
- while (q > *text && isLWSPorCR(*q)) q--;
- q++;
- newTextLen = q - *text;
- if (newTextLen != textLen) {
- textLen = newTextLen;
- MySetHandleSize(text, textLen);
- }
-
- sigLen = MyGetHandleSize(signature);
- if (sigLen > 0) {
- newTextLen = textLen + sigLen + 1;
- err = MySetHandleSize(text, newTextLen);
- if (err != noErr) goto exit;
- q = *text + textLen;
- *q++ = CR;
- BlockMoveData(*signature, q, sigLen);
- textLen = newTextLen;
- }
-
- if (textLen == 0) {
- textLen = 1;
- err = MySetHandleSize(text, textLen);
- if (err != noErr) goto exit;
- **text = ' ';
- }
-
- if (!mailIcon) to = cc = bcc = nil;
- if (!newsIcon) newsgroups = followupto = distribution = nil;
- err = MakeMailHeader(subject, to, cc, bcc, (**info).from,
- (**info).selfIcon, replyto, keywords, extraMail,
- newsgroups, followupto, distribution,
- references, &msg);
- if (err != noErr) goto exit;
- err = MyHandAndHand(text, msg);
- if (err != noErr) goto exit;
-
- *messageText = msg;
- MyDisposeHandle(text);
- return noErr;
-
- exit:
-
- MyDisposeHandle(text);
- MyDisposeHandle(msg);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MessageBodyContainsOnlyQuotedText
-
- Check to see if a message body contains only quoted text.
-
- Entry: text = handle to message body text.
- quoteString = handle to quote string.
-
- Exit: function result = true if message body contains only quoted text
- and blank lines (not counting the beginning attribution line).
- ----------------------------------------------------------------------------*/
-
- static Boolean MessageBodyContainsOnlyQuotedText (Handle text, Handle quoteString)
- {
- long textLen, quoteStringLen;
- char *p, *pEnd, *r;
- CStr255 attributionStr1, attributionStr3;
- short attributionStr1Len, attributionStr3Len;
-
- textLen = MyGetHandleSize(text);
- quoteStringLen = MyGetHandleSize(quoteString);
- if (quoteStringLen == 0) return false;
-
- GetCString(kStrQuoteStr1, attributionStr1);
- GetCString(kStrQuoteStr3, attributionStr3);
- attributionStr1Len = strlen(attributionStr1);
- attributionStr3Len = strlen(attributionStr3);
-
- p = *text;
- pEnd = p + textLen;
-
- /* Check the first line of the message body. If it is the attribution line
- "In article <...>, so-and-so wrote:", then skip over it. */
-
- r = p;
- while (r < pEnd && *r != CR) r++;
- if (r - p > attributionStr1Len + attributionStr3Len + 10) {
- if (MyStrNEqual(p, attributionStr1, attributionStr1Len) &&
- *(p + attributionStr1Len) == '<' &&
- MyStrNEqual(r - attributionStr3Len, attributionStr3, attributionStr3Len))
- {
- p = r+1;
- }
- }
-
- /* Check the remaining lines of the message body for only quoted and blank
- lines. */
-
- while (p < pEnd) {
- r = p;
- while (r < pEnd && isLWSP(*r)) r++;
- if (r >= pEnd) return true;
- if (*r == CR) { /* all blank line */
- p = r+1;
- continue;
- }
- if (p + quoteStringLen > pEnd) return false;
- if (!MyStrNEqual(p, *quoteString, quoteStringLen)) return false;
- p = r+1;
- while (p < pEnd && *p != CR) p++;
- p++;
- }
-
- return true;
- }
-
-
-
-
- /*----------------------------------------------------------------------------
- MakeAlsoPostedToText
-
- Construct the "(A copy of this message was also posted to...)" text for
- a mail message which is also being posted.
-
- Entry: headers = handle to header text for mail message.
-
- Exit: function result = error code.
- *alsoPostedTo = handle to extra "also posted to" text to
- be added to beginning of mail message.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakeAlsoPostedToText (Handle headers, Handle *alsoPostedTo)
- {
- CStr255 str;
- OSErr err = noErr;
- Handle h = nil;
- Handle newsgroups = nil;
-
- GetCString(kStrCopyAlsoPostedTo, str);
- err = MyPtrToHand(str, &h, strlen(str));
- if (err != noErr) goto exit;
- err = FindHeaderHandle(headers, "Newsgroups", &newsgroups);
- if (err != noErr) goto exit;
- if (newsgroups != nil) {
- Munger(newsgroups, 0, ",", 1, ", ", 2);
- err = MemError();
- if (err != noErr) goto exit;
- err = MyHandAndHand(newsgroups, h);
- if (err != noErr) goto exit;
- }
- err = MyStrAndHand(")\r\r", h);
- if (err != noErr) goto exit;
- Wrap(h, 0, MyGetHandleSize(h));
- MyDisposeHandle(newsgroups);
- *alsoPostedTo = h;
- return noErr;
-
- exit:
-
- MyDisposeHandle(h);
- MyDisposeHandle(newsgroups);
- return err;
- }
-
-
-
-
- /*----------------------------------------------------------------------------
- SendMessage
-
- Send a message.
-
- Entry: wind = pointer to message window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr SendMessage (WindowPtr wind)
- {
- TWindow **info;
- TEHandle theTE;
- Handle text = nil;
- Handle msg = nil;
- Handle newsgroups, to, subject, cc, bcc, replyto;
- Handle followupto, keywords, distribution;
- Handle extraNews, extraMail, signature, references;
- Handle alsoPostedTo = nil;
- OSErr err = noErr, err1 = noErr;
- long sigLen, textLen, newTextLen;
- char *q;
- Boolean newsIcon, mailIcon, selfIcon;
- short item;
- CStr255 idStr;
- CStr255 statusMsg;
- FSSpec fSpec;
- Boolean wasChanged;
- Boolean posted = false, postIndeterminate = false, mailStarted = false;
- DialogPtr dlg = nil;
-
- info = (TWindow**)GetWRefCon(wind);
- theTE = (**info).theTE;
- text = (**theTE).hText;
- newsgroups = (**(**info).newsgroupsField).hText;
- to = (**(**info).toField).hText;
- subject = (**(**info).subjectField).hText;
- cc = (**(**info).ccField).hText;
- bcc = (**(**info).bccField).hText;
- replyto = (**(**info).replytoField).hText;
- followupto = (**(**info).followuptoField).hText;
- keywords = (**(**info).keywordsField).hText;
- distribution = (**(**info).distributionField).hText;
- extraNews = (**(**info).extraNewsField).hText;
- extraMail = (**(**info).extraMailField).hText;
- signature = (**(**info).signatureField).hText;
- references = (**info).references;
- newsIcon = (**info).newsIcon;
- mailIcon = (**info).mailIcon;
- selfIcon = (**info).selfIcon;
-
- if (!newsIcon && !mailIcon && !selfIcon) {
- ErrorMessageNumber(kStrMustCheckAnIcon);
- return userCanceledErr;
- }
-
- if (HeaderLineIsEmpty(subject)) {
- ErrorMessageNumber(kStrMustSupplySubject);
- return userCanceledErr;
- }
-
- if (newsIcon && HeaderLineIsEmpty(newsgroups)) {
- ErrorMessageNumber(kStrMustSupplyNewsgroup);
- return userCanceledErr;
- }
-
- if (mailIcon && !selfIcon &&
- HeaderLineIsEmpty(to) && HeaderLineIsEmpty(cc) && HeaderLineIsEmpty(bcc))
- {
- ErrorMessageNumber(kStrMustSupplyRecipient);
- return userCanceledErr;
- }
-
- err = MyHandToHand(&text);
- if (err != noErr) return err;
-
- textLen = MyGetHandleSize(text);
- q = *text + textLen - 1;
- while (q > *text && isLWSPorCR(*q)) q--;
- q++;
- newTextLen = q - *text;
- if (newTextLen != textLen) {
- textLen = newTextLen;
- MySetHandleSize(text, textLen);
- }
-
- if (textLen == 0) {
- ErrorMessageNumber(kStrMustSupplyBody);
- err = userCanceledErr;
- goto exit;
- }
-
- if (newsIcon && (**info).isFollowup &&
- MessageBodyContainsOnlyQuotedText(text, (**(**info).quoteStringField).hText))
- {
- ErrorMessageNumber(kStrOnlyQuotedText);
- err = userCanceledErr;
- goto exit;
- }
-
- if (newsIcon) {
- err = AreYouSure();
- if (err != noErr) goto exit;
- }
-
- if ((**info).wrapOnSend) Wrap(text, 0, textLen);
-
- sigLen = MyGetHandleSize(signature);
- if (sigLen > 0) {
- newTextLen = textLen + sigLen + 1;
- err = MySetHandleSize(text, newTextLen);
- if (err != noErr) goto exit;
- q = *text + textLen;
- *q++ = CR;
- BlockMoveData(*signature, q, sigLen);
- textLen = newTextLen;
- }
-
- if (newsIcon) {
- GetCString(kStrPostingMessageStatusMsg, statusMsg);
- err = DisplayStatusMessage(statusMsg);
- if (err != noErr) goto exit;
- err = MakeNewsHeader(newsgroups, subject, replyto,
- followupto, keywords, distribution, extraNews,
- nil, references, &msg);
- if (err != noErr) goto exit;
- FindHeaderCString(msg, "Message-ID", idStr, sizeof(idStr));
- err = MyHandAndHand(text, msg);
- if (err != noErr) goto exit;
- MapMacToLatin1Handle(msg);
- err = PostArticle(msg, statusMsg, &postIndeterminate);
- posted = err == noErr || postIndeterminate;
- if (err != noErr) goto exit;
- MyDisposeHandle(msg);
- msg = nil;
- }
-
- if (mailIcon || selfIcon) {
- mailStarted = true;
- err = DisplayStatusMessageNumber(kStrMailingMessageStatusMsg);
- if (err != noErr) goto exit;
- if (!mailIcon) to = cc = bcc = nil;
- if (!newsIcon) newsgroups = followupto = distribution = nil;
- err = MakeMailHeader(subject, to, cc, bcc, (**info).from,
- (**info).selfIcon, replyto, keywords, extraMail,
- newsgroups, followupto, distribution,
- references, &msg);
- if (err != noErr) goto exit;
- if (mailIcon && newsIcon) {
- err = MakeAlsoPostedToText(msg, &alsoPostedTo);
- if (err != noErr) goto exit;
- err = MyHandAndHand(alsoPostedTo, msg);
- if (err != noErr) goto exit;
- MyDisposeHandle(alsoPostedTo);
- alsoPostedTo = nil;
- }
- err = MyHandAndHand(text, msg);
- if (err != noErr) goto exit;
- MapMacToLatin1Handle(msg);
- err = SendMailMessage(msg);
- if (err != noErr) goto exit;
- }
-
- (**info).changed = false;
- MyDisposeHandle(text);
- MyDisposeHandle(msg);
-
- if ((**info).alias != nil && gPrefs.savedMsgDelAfterSend) {
- err = ResolveAlias(nil, (**info).alias, &fSpec, &wasChanged);
- if (err == noErr) {
- (**info).alias = nil;
- err = FSpDelete(&fSpec);
- if (err != noErr) return err;
- }
- }
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(text);
- MyDisposeHandle(msg);
- MyDisposeHandle(alsoPostedTo);
- if (gCancel && posted) {
- gCancel = false;
- UncheckNewsIcon(wind);
- err1 = MyGetNewDialog(kPostCancelAlert, ok, cancel, &dlg);
- if (err1 != noErr) return err1;
- SysBeep(0);
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- err1 = DoClose(dlg);
- if (err1 != noErr) return err1;
- if (item == cancel) {
- GetCString(kStrCancelingStatusMsg, statusMsg);
- err1 = DisplayStatusMessage(statusMsg);
- if (err1 != noErr) return err1;
- err1 = CancelArticle(idStr, newsgroups, statusMsg);
- if (err1 != noErr) return err1;
- }
- } else if (posted && mailStarted) {
- UncheckNewsIcon(wind);
- ReportSystemError(err);
- NoteMessageNumber(kStrPostOKMailErr);
- return userCanceledErr;
- }
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SendMessageAndCloseWindow
-
- Send a message and close the message window.
-
- Entry: wind = pointer to message window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr SendMessageAndCloseWindow (WindowPtr wind)
- {
- OSErr err = noErr;
-
- err = SendMessage(wind);
- if (err != noErr) return err;
- return DoClose(wind);
- }
-